/*
 *  Copyright 2010-2011 Mark Eschbach
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 * $HeadURL: https://meschbach.com/svn/software/mee-utils/trunk/psi-jetty/src/main/java/com/meschbach/psi/jetty/JettyContextManagerClient.java $
 * $Id: JettyContextManagerClient.java 2836 2011-01-27 20:25:23Z meschbach $
 */
package com.meschbach.psi.jetty;

import com.meschbach.psi.Container;
import com.meschbach.psi.PSIException;
import java.io.IOException;
import java.io.InputStream;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.InputStreamBody;
import org.apache.http.impl.client.DefaultHttpClient;

/**
 * A <code>JettyContextManagerClient</code> is an HTTP client to a remote
 * <code>ContextManager</code> allowing for deployment.<p>
 *
 * This class is copyright 2010-2011 by Mark Eschbach and is licensed under the
 * Apache License, Version 2.0; accessible at
 * http://www.apache.org/licenses/LICENSE-2.0.<p>
 * 
 * @author "Mark Eschbach" &lt;meschbach@gmail.com&gt;
 * @since 1.0.0
 * @version 1.5.1
 */
public class JettyContextManagerClient implements Container {

    /**
     * The <code>server</code> contains the base remote URL for the exported
     * <code>ContextManger</code>.
     */
    protected String server;
    /**
     * The <code>contextBase</code> is the context the remote RESTful style
     * Web Application manager has been deployed too.  This is relative to
     * the <code>server</code> field of this object.
     */
    protected String contextBase;

    /**
     * Constructs a new <code>JettyContextManagerClient</code> to interact with
     * the specified remote Jetty instance at <code>server</ocde> with a
     * context manager at the specified <code>contextBase</ocde>.
     * 
     * @param server is the URL for the server to deploy too
     * @param contextBase is the context base for the remote deployment manager
     */
    public JettyContextManagerClient(String server, String contextBase) {
        this.server = server;
        this.contextBase = contextBase;
    }

    /**
     * This is a utility method to provide a decent message when the remote
     * deployment manage fails to function as expected.
     * 
     * @param message is a user defined description
     * @param stat is the status line returned by the remote manager
     * @throws PSIException will be thrown with the information given
     */
    protected void throwStatusException(String message, StatusLine stat) throws PSIException {
        throw new PSIException(message + ": " + stat.getStatusCode() + " " + stat.getReasonPhrase());
    }

    /**
     * Deploys the given <code>input</code> containing a WAR to the the specified
     * <code>aContextPath</code>.
     * 
     * @param aContextPath is the context path to deploy too
     * @param input is an input stream containing the WAR to be deployed
     * @throws PSIException if a problem occurs while deploying
     */
    public void deploy(final String aContextPath, final InputStream input) throws PSIException {
        /*
         * Configure our HTTP client
         */
        DefaultHttpClient dhc = new DefaultHttpClient();
        try {
            /*
             * Construct our request
             */
            //entity
            MultipartEntity entity = new MultipartEntity();
            System.out.println("Uploading context " + aContextPath);
            entity.addPart("PSI-war", new InputStreamBody(input, "PSI-war"));
            //POST
            HttpPost post = new HttpPost(contextBase + "/remote-deploy" + aContextPath);
            post.setEntity(entity);
            //Issue
            final HttpResponse response;
            try {
                response = dhc.execute(post);
            } catch (IOException ioe) {
                throw new PSIException(ioe);
            }
            /*
             * Ensure we recieved a valid response
             */
            StatusLine stat = response.getStatusLine();
            if (stat.getStatusCode() != 202) {
                throwStatusException("Remtoe service refused to deploy file to context path '" + aContextPath + "'", stat);
            }
        } finally {
            /*
             * Clean up our client
             */
            dhc.getConnectionManager().shutdown();
        }
    }

    /**
     * Attempts to shutdown the remote instance
     * @throws PSIException if a problem occurs while attempting to communicate
     */
    public void shutdown() throws PSIException {
        try {
            /*
             * Configure our Web Client
             */
            DefaultHttpClient dhc = new DefaultHttpClient();
            try {
                /*
                 * Issue the request
                 */
                HttpGet req = new HttpGet(contextBase + "/shutdown");
                HttpResponse response = dhc.execute(req);
                /*
                 * Ensure the operation succeeded
                 */
                StatusLine stat = response.getStatusLine();
                if (stat.getStatusCode() != 202) {
                    throw new PSIException("The server did not accept the request: " + stat.getStatusCode() + "" + stat.getReasonPhrase());
                }
            } finally {
                /*
                 * Shutdown our HTTP client
                 */
                dhc.getConnectionManager().shutdown();
            }
        } catch (IOException ioe) {
            throw new PSIException("Unable to shutdown remote broker", ioe);
        }
    }

    /**
     * Retrieves the base URL of the remote server.  Combine this with the
     * context path and you have the URL of the remotely deployed web application.
     * 
     * @return the remote URL of the server
     */
    public String getRemoteURL() {
        return server;
    }
}
